Repository Public
1import Link from "next/link";2import { FilmCard } from "@/components/FilmCard";3import { HeroPlayer } from "@/components/HeroPlayer";4import { catalogStats, heroFilm, listCollections, listFilms, listSeries } from "@/lib/films";5import { dict, fmtDuration, href, type Locale } from "@/lib/i18n";67export default async function Home({ params }: { params: Promise<{ locale: string }> }) {8 const locale = (await params).locale as Locale;9 const t = dict(locale);10 const [hero, latest, top, series, collections, stats] = await Promise.all([11 heroFilm(locale), listFilms(locale, { limit: 10 }), listFilms(locale, { limit: 5, sort: "top" }), listSeries(locale), listCollections(locale), catalogStats(),12 ]);1314 return (15 <>16 {/* First viewport: an actual playable film, or an honest empty state. */}17 <section className="grid items-center gap-10 py-8 md:grid-cols-[minmax(0,1fr)_auto] md:gap-16 md:py-14">18 <div className="rise order-2 md:order-1">19 <p className="kicker">{hero ? t.home.kicker : t.home.soon}</p>20 {hero ? (21 <>22 {hero.series && <p className="accent-text mt-6 text-sm font-semibold uppercase tracking-[0.16em]" style={{ "--a": hero.series.accent } as React.CSSProperties}>{hero.series.title}{hero.episode ? ` · ${hero.episode}` : ""}</p>}23 <h1 className="display mt-3 text-[clamp(3rem,8vw,6.5rem)]">{hero.title}</h1>24 <p className="prose-film mt-6 text-lg">{hero.synopsis}</p>25 <div className="mt-8 flex flex-wrap items-center gap-3">26 <Link href={href(locale, `/film/${hero.slug}`)} className="btn btn-primary">{t.home.watch}</Link>27 <Link href={href(locale, "/feed")} className="btn btn-ghost">{t.home.openFeed}</Link>28 <span className="ml-1 text-sm tabular-nums text-[var(--color-faint)]">{fmtDuration(hero.duration)} · {t.film.aiDisclosure}</span>29 </div>30 </>31 ) : (32 <>33 <h1 className="display mt-6 text-[clamp(3rem,8vw,6rem)]">{t.home.emptyTitle}</h1>34 <p className="prose-film mt-6 text-lg">{t.home.emptyBody}</p>35 <div className="mt-8"><Link href={href(locale, "/about")} className="btn btn-ghost">{t.home.processLink}</Link></div>36 </>37 )}38 </div>39 <div className="order-1 mx-auto w-full max-w-[440px] md:order-2 md:w-[min(34vw,400px)]">40 <div className="frame relative aspect-[9/16]">41 {hero ? <HeroPlayer film={hero} labels={t.player} locale={locale} /> : (42 <div className="grid h-full place-items-center bg-[radial-gradient(circle_at_50%_40%,#2a2418,transparent_60%)]">43 <div className="h-3 w-3 rounded-full bg-[var(--color-lamp)] shadow-[0_0_60px_18px_rgba(232,194,122,0.35)]" />44 </div>45 )}46 </div>47 </div>48 </section>4950 {latest.films.length > 0 && (51 <section className="mt-14" aria-labelledby="latest">52 <div className="mb-5 flex items-end justify-between gap-4">53 <div><h2 id="latest" className="display text-3xl md:text-4xl">{t.home.latest}</h2><p className="mt-1 text-sm text-[var(--color-faint)]">{t.home.latestSub}</p></div>54 <Link href={href(locale, "/explore")} className="btn btn-ghost btn-sm">{t.nav.explore}</Link>55 </div>56 <div className="rail -mx-4 px-4 sm:-mx-6 sm:px-6">57 {latest.films.map((f, i) => <div key={f.id} className="w-[46vw] sm:w-[220px]"><FilmCard film={f} locale={locale} priority={i < 5} /></div>)}58 </div>59 </section>60 )}6162 {collections.map((c) => (63 <section key={c.slug} className="mt-16" aria-label={c.title}>64 <div className="mb-5"><p className="label">Collection</p><h2 className="display mt-1 text-4xl">{c.title}</h2><p className="mt-1 text-sm text-[var(--color-faint)]">{c.description}</p></div>65 <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-4 lg:grid-cols-5">{c.films.slice(0, 5).map((f) => <FilmCard key={f.id} film={f} locale={locale} />)}</div>66 </section>67 ))}6869 <section className="mt-16" aria-labelledby="series">70 <div className="mb-5"><h2 id="series" className="display text-3xl md:text-4xl">{t.home.series}</h2><p className="mt-1 text-sm text-[var(--color-faint)]">{t.home.seriesSub}</p></div>71 <div className="grid gap-px overflow-hidden rounded-[22px] border border-[var(--color-line-soft)] bg-[var(--color-line-soft)] sm:grid-cols-2 lg:grid-cols-4">72 {series.map((s) => (73 <Link key={s.slug} href={href(locale, `/series/${s.slug}`)} className="group relative flex min-h-[190px] flex-col justify-between bg-[var(--color-stage)] p-5 transition-colors hover:bg-[var(--color-panel)]">74 <div className="flex items-center justify-between">75 <span className="h-2 w-2 rounded-full" style={{ background: s.accent }} />76 <span className="text-xs tabular-nums text-[var(--color-faint)]">{s.count > 0 ? `${s.count} ${t.seriesPage.episodes}` : t.seriesPage.upcoming}</span>77 </div>78 <div><h3 className="display text-2xl">{s.title}</h3><p className="mt-2 text-sm leading-relaxed text-[var(--color-muted)]">{s.description}</p></div>79 </Link>80 ))}81 </div>82 </section>8384 {top.films.length > 2 && (85 <section className="mt-16" aria-labelledby="top">86 <div className="mb-5"><h2 id="top" className="display text-3xl md:text-4xl">{t.home.top}</h2><p className="mt-1 text-sm text-[var(--color-faint)]">{t.home.topSub}</p></div>87 <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 sm:gap-4 lg:grid-cols-5">{top.films.map((f) => <FilmCard key={f.id} film={f} locale={locale} note={f.qualityScore ? `${f.qualityScore.toFixed(1)} / 10` : undefined} />)}</div>88 </section>89 )}9091 <section className="mt-20 grid gap-10 border-t border-[var(--color-line-soft)] pt-12 md:grid-cols-[1.2fr_1fr]" aria-labelledby="process">92 <div>93 <p className="kicker">{t.home.process}</p>94 <h2 id="process" className="display mt-4 text-4xl md:text-5xl">{t.about.title}</h2>95 <p className="prose-film mt-5 text-lg">{t.home.processBody}</p>96 <Link href={href(locale, "/about")} className="btn btn-ghost mt-7">{t.home.processLink}</Link>97 </div>98 <dl className="grid grid-cols-3 content-center gap-4">99 {([[stats.films, t.home.stats.films], [(stats.seconds / 60).toFixed(1), t.home.stats.minutes], [stats.shots, t.home.stats.shots]] as const).map(([n, label]) => (100 <div key={label} className="border-l border-[var(--color-line)] pl-4"><dd className="display text-5xl tabular-nums">{n}</dd><dt className="mt-2 text-xs uppercase tracking-[0.14em] text-[var(--color-faint)]">{label}</dt></div>101 ))}102 </dl>103 </section>104 </>105 );106}107